package com.woniu.mall.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.mall.VO.CartVO;
import com.woniu.mall.VO.ProductDetailVO;
import com.woniu.mall.config.MallOrderQueueConfig;
import com.woniu.common.utils.IdWorker;
import com.woniu.mall.DTO.MallOrderDTO;
import com.woniu.mall.DTO.MallProductDTO;
import com.woniu.mall.VO.*;
import com.woniu.mall.constant.*;
import com.woniu.mall.constant.BusinessRedisKey;
import com.woniu.mall.constant.CartRedisKey;
import com.woniu.mall.constant.OrderConstants;
import com.woniu.mall.constant.SeckillRedisKey;
import com.woniu.mall.entity.CheckOrderInfo;
import com.woniu.mall.constant.PlatformRedisKey;
import com.woniu.mall.mapper.MallOrderMapper;
import com.woniu.mall.pojo.Coupon;
import com.woniu.mall.pojo.MallOrder;
import com.woniu.mall.pojo.MallSeckill;
import com.woniu.mall.pojo.MallProduct;
import com.woniu.mall.service.CouponPlatformService;
import com.woniu.mall.service.IMallOrderService;
import com.woniu.mall.service.IMallProductService;
import com.woniu.pay.entity.PayInfo;
import com.woniu.pay.feign.PayFeignClient;
import com.woniu.user.feignClient.UserFeignClient;
import com.woniu.user.pojo.User;
import com.woniu.user.feignClient.AddressFeignClient;
import com.woniu.user.pojo.Address;
import com.woniu.user.vo.UserLoginVO;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;


import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.text.DecimalFormat;
import java.util.Map;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 坤
 * @since 2023-05-22
 */
@Service
@Slf4j
public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder> implements IMallOrderService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IMallProductService productService;
    @Resource
    private MallOrderMapper orderMapper;
    @Resource
    private PayFeignClient payFeignClient;
    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AddressFeignClient addressFeignClient;

    @Autowired
    private CouponPlatformService couponPlatformService;

    @Override
    public Map selectCart(UserLoginVO userLoginVO) {
        Map cartVOMap = redisTemplate.boundHashOps(CartRedisKey.CART + userLoginVO.getMobile()).entries();

        return cartVOMap;
    }

    @Override
    public Boolean addCart(MallProductDTO mallProductDTO, UserLoginVO userLoginVO) {

        ProductDetailVO productDetailVO = productService.showDetail(mallProductDTO.getProductId());

        //判断商品库存
        if (productDetailVO.getStock() < mallProductDTO.getItemQuantity()) {
            return false;
        }

        //判断该商品是否来自该店铺
        MallProduct product = productService.getProduct(mallProductDTO.getProductId());
        if (product.getStoreId() != mallProductDTO.getStoreId()) {
            return false;
        }

        MallProductVO mallProductVO = new MallProductVO();
        mallProductVO.setStoreId(mallProductDTO.getStoreId());
        mallProductVO.setProductId(mallProductDTO.getProductId());
        mallProductVO.setItemQuantity(mallProductDTO.getItemQuantity());
        mallProductVO.setTotalPrice(productDetailVO.getPrice().multiply(new BigDecimal(mallProductDTO.getItemQuantity())));

        //加入购物车
        List<MallProductVO> mallProductVOListInRedis = (List<MallProductVO>) redisTemplate.boundHashOps(CartRedisKey.CART + userLoginVO.getMobile()).get(mallProductDTO.getStoreId().toString());

        //如果是第一次添加
        if (mallProductVOListInRedis == null) {
            List<MallProductVO> mallProductVOList = new ArrayList<>();
            Boolean flag = mallProductVOList.add(mallProductVO);
            redisTemplate.boundHashOps(CartRedisKey.CART + userLoginVO.getMobile()).put(mallProductDTO.getStoreId() + ":" + mallProductDTO.getProductId(), mallProductVO);
            return flag;
        }

        //如果不是第一次添加
        boolean flag = mallProductVOListInRedis.add(mallProductVO);
        redisTemplate.boundHashOps(CartRedisKey.CART + userLoginVO.getMobile()).put(mallProductDTO.getStoreId() + ":" + mallProductDTO.getProductId(), mallProductVO);
        return flag;
    }

    @Override
    public Boolean deleteCart(UserLoginVO userLoginVO) {

        Boolean flag = redisTemplate.delete(CartRedisKey.CART + userLoginVO.getMobile());
        return flag;
    }

    @Override
    public CartVO settlement(List<MallProductDTO> mallProductDTOList, String mobile) {

        List<MallProductVO> mallProductVOList = new ArrayList<>();
        BigDecimal totalPrice = new BigDecimal(0);

        for (MallProductDTO mallProductDTO : mallProductDTOList) {
            MallProductVO mallProductVO = (MallProductVO) redisTemplate.boundHashOps(CartRedisKey.CART + mobile).get(mallProductDTO.getStoreId() + ":" + mallProductDTO.getProductId());

            if (mallProductVO == null) {
                throw new RuntimeException(OrderConstants.NO_PRODUCT);
            }

            mallProductVOList.add(mallProductVO);
            totalPrice = totalPrice.add(mallProductVO.getTotalPrice());
        }

        CartVO cartVO = new CartVO(mallProductVOList, totalPrice);

        //判断用户是否是第一次创建订单
        Coupon tianTianCoupon = (Coupon) redisTemplate.boundHashOps(PlatformRedisKey.TIANTIAN_COUPON).get(mobile);
        if (tianTianCoupon == null) {
            Coupon createTianTianCoupon = couponPlatformService.saveTianTian();
            redisTemplate.boundValueOps(PlatformRedisKey.TIANTIAN_COUPON + mobile).set(createTianTianCoupon, 1, TimeUnit.DAYS);
        }
        return cartVO;
    }

    @SneakyThrows
    @Override
    public MallOrder createOrder(MallOrderDTO mallOrderDTO, UserLoginVO userLoginVO) {

        //同时使用美团红包和天天神券
        if (mallOrderDTO.getIsRedEnvelope() && mallOrderDTO.getIsTiantianCoupon()) {
            throw new RuntimeException(OrderConstants.CANT_USER_BOTH);
        }

        //查询redis 获取天天神券信息 天天神券已经使用过
        Coupon tianTiancoupon = (Coupon) redisTemplate.boundValueOps(PlatformRedisKey.TIANTIAN_COUPON + userLoginVO.getMobile()).get();
        if (mallOrderDTO.getIsTiantianCoupon() && !tianTiancoupon.getStatus()) {
            throw new RuntimeException(OrderConstants.CANT_USER_TIANTIAN);
        }

        //不填写地址
        if (mallOrderDTO.getAddressId() == null) {
            throw new RuntimeException(OrderConstants.NO_ADDRESS);
        }

        //创建订单对象
        MallOrder mallOrder = new MallOrder();

        //生成订单号
        IdWorker idWorker = new IdWorker();
        Integer orderNO = (int) idWorker.nextId();
        //抵扣金额
        BigDecimal deductionAmount = new BigDecimal(0);

        //设置支付状态
        mallOrder.setOrderStatus((byte) 0);

        //设置订单号
        mallOrder.setOrderId(orderNO);

        //设置用户id
        mallOrder.setUserId(userLoginVO.getId());

        //设置订单地址
        Address address = addressFeignClient.getAddress(mallOrderDTO.getAddressId(), userLoginVO.getMobile()).getData();
        mallOrder.setUserAddress(address.getDetail());

        //获取购物车信息 如果没有天天神券 则创建天天神券
        CartVO cartVO = settlement(mallOrderDTO.getProductDTOList(), userLoginVO.getMobile());

        //使用美团红包
        if (mallOrderDTO.getIsRedEnvelope()) {
            //查询redis 如果红包数量足够 减少红包个数 保存到数据库
            Coupon redEnvelope = (Coupon) redisTemplate.boundValueOps(PlatformRedisKey.RED_ENVELOPE + userLoginVO.getMobile()).get();
            //如果该用户拥有的红包数量大于0
            if (redEnvelope.getNumber() > 0) {
                //扣减一个红包
                redEnvelope.setNumber(redEnvelope.getNumber() - 1);
                redisTemplate.boundValueOps(PlatformRedisKey.RED_ENVELOPE + userLoginVO.getMobile()).set(redEnvelope);

                //设置使用一个美团红包类型 和金额
                mallOrder.setPlatformCouponName(PlatformRedisKey.RED_ENVELOPE);
                mallOrder.setPlatformCouponAmount(new BigDecimal(PlatformRedisKey.AMOUNT_5));
                //记录抵扣金额
                deductionAmount = deductionAmount.add(new BigDecimal(PlatformRedisKey.AMOUNT_5));
            }
        }

        //使用天天神券
        if (mallOrderDTO.getIsTiantianCoupon()) {
            //如果天天神券未被使用
            if (tianTiancoupon.getStatus()) {
                //修改天天神券redis
                tianTiancoupon.setStatus(false);
                redisTemplate.boundValueOps(PlatformRedisKey.TIANTIAN_COUPON + userLoginVO.getMobile()).set(tianTiancoupon);

                mallOrder.setPlatformCouponName(PlatformRedisKey.TIANTIAN_COUPON);
                mallOrder.setPlatformCouponAmount(tianTiancoupon.getAmount());
            }
        }

        //获取分区优惠券列表
        List<Coupon> zoneCouponList = mallOrderDTO.getZoneCouponList();
        //是否使用分区优惠券
        BigDecimal zoneCouponAmount = new BigDecimal(0);
        if (zoneCouponList != null) {
            for (Coupon zoneCoupon : zoneCouponList) {
                redisTemplate.boundHashOps(PlatformRedisKey.Zone_COUPON + userLoginVO.getMobile()).delete(zoneCoupon.getName() + zoneCoupon.getAmount().toString());
                deductionAmount = deductionAmount.add(zoneCoupon.getAmount());
                zoneCouponAmount = zoneCouponAmount.add(zoneCoupon.getAmount());
            }
            List<String> zoneCouponNameList = zoneCouponList.stream().map(coupon -> coupon.getName()).collect(Collectors.toList());
            mallOrder.setZoneCouponName(zoneCouponNameList.toString());
            mallOrder.setZoneCouponAmount(zoneCouponAmount);
        }

        //是否使用商家代金券
        List<Integer> storeCouponIdList = mallOrderDTO.getStoreCouponIdList();

        if (storeCouponIdList != null) {

            int totalDeleteId = 0;
            for (Integer storeId : storeCouponIdList) {
                Object storeCoupon = redisTemplate.boundHashOps(BusinessRedisKey.BUSINESS_COUPON + storeId).get(userLoginVO.getMobile());
                if (storeCoupon == null) {
                    throw new RuntimeException(OrderConstants.NO_BUSINESSCOUPON);
                }

                Coupon businessCoupon = (Coupon) redisTemplate.boundHashOps(BusinessRedisKey.BUSINESS_COUPON + storeId).get(userLoginVO.getMobile());
                if (businessCoupon.getNumber() > 0){
                    businessCoupon.setNumber(businessCoupon.getNumber() - 1);
                    redisTemplate.boundHashOps(BusinessRedisKey.BUSINESS_COUPON + storeId).put(userLoginVO.getMobile(),businessCoupon);
                    if (businessCoupon.getNumber() == 0){
                        redisTemplate.boundHashOps(BusinessRedisKey.BUSINESS_COUPON + storeId).delete(userLoginVO.getMobile());
                    }
                }
                totalDeleteId = totalDeleteId + 1;
            }
            BigDecimal bussinessCouponDeductionAmount = new BigDecimal(totalDeleteId).multiply(BusinessRedisKey.AMOUNT_4);
            deductionAmount = deductionAmount.add(bussinessCouponDeductionAmount);

            mallOrder.setStoreCouponAmount(bussinessCouponDeductionAmount);
        }

        //设置订单总金额
        mallOrder.setTotalPrice(cartVO.getTotalPrice().subtract(deductionAmount));

        //判断库存是否足够 超卖问题
        //获取所有商品id
        List<MallProductDTO> productDTOList = mallOrderDTO.getProductDTOList();
        List<Integer> productIdList = productDTOList.stream().map(mallProductDTO -> mallProductDTO.getProductId()).collect(Collectors.toList());

        //判断库存是否足够 足够下单 不足抛异常
        for (MallProductDTO mallProductDTO : productDTOList) {
            Integer stock = (Integer) redisTemplate.boundHashOps(OrderConstants.STOCK).get(mallProductDTO.getStoreId() + ":" + mallProductDTO.getProductId());

            if (mallProductDTO.getItemQuantity() > stock) {
                return null;
            } else {
                redisTemplate.boundHashOps(OrderConstants.STOCK).put(mallProductDTO.getStoreId() + ":" + mallProductDTO.getProductId(), stock - mallProductDTO.getItemQuantity());
                productService.updateStock(mallProductDTO.getProductId(), mallProductDTO.getItemQuantity());
            }
        }

        //删除购物车
        redisTemplate.delete(CartRedisKey.CART + userLoginVO.getMobile());

        //支付
        PayInfo payInfo = new PayInfo();
        payInfo.setOutTradeNo(mallOrder.getOrderId().toString());
        payInfo.setTotalAmount(mallOrder.getTotalPrice());
        payInfo.setSubject(productIdList.toString());
        payInfo.setNotifyUrl(MallPayInfoConstants.NOTIFY_URL);
        payInfo.setReturnUrl(MallPayInfoConstants.NOTIFY_URL);
        payFeignClient.pay(payInfo);

        //死信队列

        //订单保存到数据库
        orderMapper.insert(mallOrder);
        return mallOrder;
    }

    @Override
    public CouponVO selectCoupon(List<MallProductDTO> mallProductDTOList, String mobile) {

        Set<Coupon> availableCouponSet = new HashSet<>();
        Set<Coupon> unAvailableCouponSet = new HashSet<>();

        //查询用户的美团红包数量 如果大于1 加入可用
        Coupon redEnvelopeCoupon = (Coupon) redisTemplate.boundValueOps(PlatformRedisKey.RED_ENVELOPE + mobile).get();
        if (redEnvelopeCoupon.getNumber() > 1) {
            availableCouponSet.add(redEnvelopeCoupon);
        }

        //查询用户的天天神券
        Coupon tianTianCoupon = (Coupon) redisTemplate.boundValueOps(PlatformRedisKey.TIANTIAN_COUPON + mobile).get();

        //合计总金额 判断分区优惠券
        BigDecimal totalPrice = new BigDecimal(0);

        //查询该用户拥有的所有分区优惠券 获取优惠券名称
        List<Coupon> zongCouponList = redisTemplate.boundHashOps(PlatformRedisKey.Zone_COUPON + mobile).values();

        Set<Coupon> availableSet = new HashSet<>();
        Set<Coupon> unAvailableSet = new HashSet<>();
        for (MallProductDTO mallProductDTO : mallProductDTOList) {
            MallProductVO mallProductVO = (MallProductVO) redisTemplate.boundHashOps(CartRedisKey.CART + mobile).get(mallProductDTO.getStoreId() + ":" + mallProductDTO.getProductId());

            //判断分区优惠券是否可用
            availableSet = zongCouponList.stream().filter(coupon -> coupon.getName().equals(mallProductDTO.getStoreCategory()) && coupon.getStatus()).collect(Collectors.toSet());
            unAvailableSet = zongCouponList.stream().filter(coupon -> !coupon.getName().equals(mallProductDTO.getStoreCategory())).collect(Collectors.toSet());

            //计算总金额
            totalPrice = totalPrice.add(mallProductVO.getTotalPrice());
        }

        availableCouponSet.addAll(availableSet);
        unAvailableCouponSet.addAll(unAvailableSet);

        //如果天天神券状态为true 并且达到起用金额 加入可用
        if (tianTianCoupon.getStatus() && tianTianCoupon.getStartingAmount().compareTo(totalPrice) <= 0) {
            availableCouponSet.add(tianTianCoupon);
        } else if (tianTianCoupon.getStatus() && tianTianCoupon.getStartingAmount().compareTo(totalPrice) > 0) {
            unAvailableCouponSet.add(tianTianCoupon);
        }

        CouponVO couponVO = new CouponVO(availableCouponSet, unAvailableCouponSet);
        return couponVO;
    }

    @Override
    public void saveStock() {
        List<MallProduct> allProduct = productService.getAllProduct();
        for (MallProduct product : allProduct) {
            redisTemplate.boundHashOps(OrderConstants.STOCK).put(product.getStoreId() + ":" + product.getProductId(), product.getStock());
        }
    }

    @Override
    public Integer addOrder(MallOrder order) {
        int result = orderMapper.insert(order);
        return result;
    }

    @Override
    public Integer updateOrder(MallOrder order) {
        Integer result = orderMapper.updateById(order);
        return result;
    }

    @Override
    public MallOrder getOrderById(String outTradeNo) {
        MallOrder order = orderMapper.getOrderById(outTradeNo);
        return order;
    }

    @Override
    public AlipayTradeQueryResponse checkPayStatus(String outTradeNo) {
        Object response = payFeignClient.queryStatusByOutTradeNo(outTradeNo).getData();
        //JSON格式转换
        String jsonObject = JSON.toJSONString(response);
        AlipayTradeQueryResponse alipayTradeQueryResponse = JSONObject.parseObject(jsonObject, AlipayTradeQueryResponse.class);
        return alipayTradeQueryResponse;
    }

    /**
     * 死信队列消费者查询订单状态
     */
    @RabbitListener(queues = MallOrderQueueConfig.DLQ_ORDER_QUEUE)
    public void checkOrderTimeout(CheckOrderInfo checkOrderInfo) {
        //1. 根据订单ID查询订单在本地的状态
        String orderNo = checkOrderInfo.getOrderNo();
        MallOrder order = getOrderById(orderNo);
        //2. 如果本地不是待支付状态，不进行处理
        if (order.getOrderStatus() != OrderConstants.ORDER_STATUS_UNPAY) {
            log.debug("订单编号为：{}的订单已经处理了", orderNo);
            return;
        }
        //3. 如果是待支付状态，查询支付宝订单状态
        AlipayTradeQueryResponse response = (AlipayTradeQueryResponse) payFeignClient.queryStatusByOutTradeNo(orderNo).getData();
        //4. 如果支付宝显示也是未支付，代表订单已经超时
        if (response.getTradeStatus().equals("WAIT_BUYER_PAY") || response.getTradeStatus().equals("TRADE_CLOSED")) {
            //5. 修改订单的状态
            order.setOrderStatus(OrderConstants.ORDER_STATUS_TIMEOUT);
            //6. 返回预扣的库存
            //查询是否为秒杀的订单
            Boolean flag = redisTemplate.boundHashOps(SeckillRedisKey.SECKILL_ORDER).hasKey(checkOrderInfo.getOrderNo());
            //查询是否为比特币业务
            boolean bitCoin = checkOrderInfo.getProductName().equals(OrderConstants.RECHARGE_BIT_COIN);
            if (!flag) {
                if (bitCoin) {
                    //比特币业务
                    //获取电话号码
                    String mobile = userFeignClient.getUser(order.getUserId()).getData().getMobile();
                    //扣减充值的比特币
                    userFeignClient.updateAmount(mobile, new BigDecimal(response.getTotalAmount()));
                }
                //返回普通商品库存
                productService.addStock(checkOrderInfo.getProductId());
            }
            //返回秒杀的库存
            Integer productId = checkOrderInfo.getProductId();
            MallSeckill seckill = (MallSeckill) redisTemplate.boundHashOps(SeckillRedisKey.SECKILL_LIST).get(productId + "");
            seckill.setPsCount(seckill.getPsCount() - 1);
            redisTemplate.boundHashOps(SeckillRedisKey.SECKILL_LIST).put(productId + "", seckill);
        }
    }
}
